package script;

import java.util.ArrayList;

import main.ConditionException;
import main.ErrorException;

import define.CompareType;
import define.ConditionType;
import define.Config;
import define.ErrorDefine;

public class Condition 
{
	private String[] m_express;
	private ArrayList<Condition> m_children;
	private ConditionType m_type;
	private int m_start;
	private int m_end;
	private Function m_function;
	
	public Condition(String[] input, int start, int end, Function function) throws ConditionException
	{
		m_express = input;
		m_start = start;
		m_end = end;
		m_function = function;
		if(m_end < m_start && input.length != 0)
		{
			throw new ConditionException(ErrorDefine.E_CONDITION_EXPRESS_ERROR, m_start);
		}
		makeType();
		makeChildren();
	}
	
	private void makeType() throws ConditionException
	{
		if(m_express.length == 0)
		{
			m_type = ConditionType.E_NULL_CONDITION;
			return;
		}
		int start = findLogic(m_start);
		if(start != Config.NO_FIND)
		{
			if(m_start == m_end)
			{
				m_type = ConditionType.E_RELATION_CONDITION;
				return;
			}
			m_type = ConditionType.E_MIX_CONDITION;
			m_children = new ArrayList<>();
			return;
		}
		start = find(CompareType.E_CHILD_START_CT.getExp(), m_start);
		if(start != Config.NO_FIND)
		{
			if(start > m_start + 1 || findEnd(start) != m_end)
			{
				throw new ConditionException(ErrorDefine.E_CONDITION_EXPRESS_ERROR, start);
			}
			m_type = ConditionType.E_CHILD_CONDITION;
			m_children = new ArrayList<>();
			return;
		}
		m_type = ConditionType.E_EXPRESS_CONDITION;
	}
	
	private void makeChildren() throws ConditionException
	{
		switch(m_type) 
		{
		case E_MIX_CONDITION:
			makeMixChildren();
			break;
		case E_CHILD_CONDITION:
			makeChildChildren();
			break;
		default:
			break;
		}
	}
	
	private void makeMixChildren() throws ConditionException
	{
		int start = m_start;
		while(start <= m_end) 
		{
			int end = findLogic(start);
			if(end == m_start || end == m_end)
			{
				throw new ConditionException(ErrorDefine.E_CONDITION_EXPRESS_ERROR, end);
			}
			if(end == Config.NO_FIND)
			{
				end = m_end + 1;
			}
			m_children.add(new Condition(m_express, start, end - 1, m_function));
			if(end <= m_end)
			{
				m_children.add(new Condition(m_express, end, end, m_function));
			}
			start = end + 1;
		}
	}
	
	private void makeChildChildren() throws ConditionException
	{
		if(m_express[m_start].compareTo(CompareType.E_NO_CT.getExp()) == 0)
		{
			m_children.add(new Condition(m_express, m_start, m_start, m_function));
			m_children.add(new Condition(m_express, m_start + 2, m_end - 1, m_function));
		}
		else if(m_express[m_start].compareTo(CompareType.E_CHILD_START_CT.getExp()) == 0)
		{
			m_children.add(new Condition(m_express, m_start + 1, m_end - 1, m_function));	
		}
		else 
		{
			throw new ConditionException(ErrorDefine.E_CONDITION_EXPRESS_ERROR, m_start);
		}
		
	}
	
	private int find(String aim, int start)
	{
		for(; start <= m_end; ++start)
		{
			if(aim.compareTo(m_express[start]) == 0)
			{
				return start;
			}
		}
		return Config.NO_FIND;
	}
	
	private int findLogic(int start) throws ConditionException
	{
		for(; start <= m_end; ++start)
		{
			if(m_express[start].compareTo(CompareType.E_CHILD_END_CT.getExp()) == 0)
			{
				throw new ConditionException(ErrorDefine.E_CONDITION_EXPRESS_NOT_CLOSE_ERROR, start);
			}
			if(m_express[start].compareTo(CompareType.E_CHILD_START_CT.getExp()) == 0)
			{
				start = findEnd(start);
				if(start == Config.NO_FIND)
				{
					throw new ConditionException(ErrorDefine.E_CONDITION_EXPRESS_NOT_CLOSE_ERROR, start);
				}
			}
			else if(CompareType.isLogic(m_express[start]))
			{
				return start;
			}
		}
		return Config.NO_FIND;
	}
	
	private int findEnd(int start)
	{
		int count = 1;
		for(++start; start <= m_end; ++start)
		{
			if(m_express[start].compareTo(CompareType.E_CHILD_START_CT.getExp()) == 0)
			{
				++count;
			}
			else if(m_express[start].compareTo(CompareType.E_CHILD_END_CT.getExp()) == 0)
			{
				--count;
				if(count == 0)
				{
					return start;
				}
			}
		}
		return Config.NO_FIND;
	}
	
	public boolean getValue() throws ConditionException, ErrorException
	{
		switch(m_type) 
		{
		case E_MIX_CONDITION:
			return getMixValue();
		case E_CHILD_CONDITION:
			return getChildValue();
		case E_EXPRESS_CONDITION:
			return getExpressValue();
		case E_RELATION_CONDITION:
			assert false: "can't get relationship express value";
		case E_NULL_CONDITION:
			return false;
		default:
			return false;
		}
	}
	
	private boolean getMixValue() throws ConditionException, ErrorException
	{
		boolean value = false;
		for(int i = 0, max = m_children.size(); i < max; ++i)
		{
			Condition con = m_children.get(i);
			if(con.getType() != ConditionType.E_RELATION_CONDITION)
			{
				value = con.getValue();
			}
			else
			{
				if(i == max - 1 || i == 0)
				{
					throw new ConditionException(ErrorDefine.E_CONDITION_EXPRESS_ERROR, con.getStart());
				}
				CompareType ct = CompareType.getCompare(m_express[con.getStart()]);
				if(ct == null)
				{
					throw new ConditionException(ErrorDefine.E_NO_CONDITION_ERROR, con.getStart());
				}
				value = ct.getLogicValue(value, m_children.get(i + 1).getValue(), con.getStart());
				++i;
			}
		}
		return value;
	}
	
	private boolean getChildValue() throws ConditionException, ErrorException
	{
		if(m_children.size() == 1)
		{
			return m_children.get(0).getValue();
		}
		return !m_children.get(1).getValue();
	}
	
	private boolean getExpressValue() throws ConditionException, ErrorException
	{
		int size = m_end - m_start + 1;
		if(size == 1)
		{
			CompareType ct = CompareType.getCompare(m_express[m_start]);
			if(ct != null)
			{
				throw new ConditionException(ErrorDefine.E_CONDITION_EXPRESS_ERROR, m_start);
			}
			return m_express[m_start].compareTo(Config.FALSE_CONDITION) != 0;
		}
		else if(size == 2)
		{
			CompareType ct1 = CompareType.getCompare(m_express[m_start]);
			CompareType ct2 = CompareType.getCompare(m_express[m_end]);
			if(ct1 != CompareType.E_NO_CT || ct2 != null)
			{
				throw new ConditionException(ErrorDefine.E_CONDITION_EXPRESS_ERROR, m_start);
			}
			return m_express[m_end].compareTo(Config.FALSE_CONDITION) == 0;
		}
		else if(size == 3)
		{
			CompareType ct1 = CompareType.getCompare(m_express[m_start]);
			CompareType ct2 = CompareType.getCompare(m_express[m_start + 1]);
			CompareType ct3 = CompareType.getCompare(m_express[m_end]);
			if(ct1 != null || ct3 != null || ct2 == null)
			{
				throw new ConditionException(ErrorDefine.E_CONDITION_EXPRESS_ERROR, m_start);
			}
			if(ct2 == CompareType.E_HAS_CT)
			{
				return m_function.compareHas(m_start + 1, true);
			}
			else if(ct2 == CompareType.E_UNHAS_CT)
			{
				return m_function.compareHas(m_start + 1, false);
			}
			return ct2.getCompareValue(m_express[m_start], m_express[m_end], m_start + 1);
		}
		else
		{
			throw new ConditionException(ErrorDefine.E_CONDITION_EXPRESS_ERROR, m_start);
		}
	}
	
	public ConditionType getType()
	{
		return m_type;
	}
	
	public int getStart()
	{
		return m_start;
	}
}
